package dot;

import java.io.PrintWriter;
import java.io.Writer;

import org.jgrapht.DirectedGraph;
import org.jgrapht.Graph;
import org.jgrapht.ext.EdgeNameProvider;
import org.jgrapht.ext.IntegerNameProvider;
import org.jgrapht.ext.VertexNameProvider;

public class DOTExporter<V, E> {
	/**
	 * Defenition of writer's DOT format.
	 */
	private VertexNameProvider<V> vertexIDProvider;
	private VertexNameProvider<V> vertexLabelProvider;
	private EdgeNameProvider<E> edgeLabelProvider;

	/**
	 * Constructs a new DOTExporter object with an integer name provider for the
	 * vertex IDs and null providers for the vertex and edge labels.
	 */
	public DOTExporter() {
		this(new IntegerNameProvider<V>(), null, null);
	}

	/**
	 * Constructs a new DOTExporter object with the given ID and attributes
	 * 
	 * @param vertexIDProvider
	 *            for generating vertex IDs. Must not be null.
	 * @param vertexLabelProvider
	 *            for generating vertex labels. If null, vertex labels will not
	 *            be written to the file.
	 * @param edgeLabelProvider
	 *            for generating edge labels. If null, edge labels will not be
	 *            written to the file.
	 */
	public DOTExporter(VertexNameProvider<V> vertexIDProvider,
			VertexNameProvider<V> vertexLabelProvider,
			EdgeNameProvider<E> edgeLabelProvider) {
		this.vertexIDProvider = vertexIDProvider;
		this.vertexLabelProvider = vertexLabelProvider;
		this.edgeLabelProvider = edgeLabelProvider;
	}

	/**
	 * Exports a graph into a plain text file in DOT format.
	 * 
	 * @param writer
	 *            the writer to which the graph to be exported
	 * @param g
	 *            the graph to be exported
	 */
	public void export(Writer writer, Graph<V, E> g) {
		PrintWriter out = new PrintWriter(writer);
		String indent = "  ";
		String connector;

		if (g instanceof DirectedGraph) {
			out.println("digraph G {");
			connector = " -> ";
		} else {
			out.println("graph G {");
			connector = " -- ";
		}

		out.println("node [	color = white,style = filled ];");

		for (V v : g.vertexSet()) {
			out.print(indent + getVertexID(v));

			if (vertexLabelProvider != null) {
				out.print(vertexLabelProvider.getVertexName(v));
			}

			out.println(";");
		}

		for (E e : g.edgeSet()) {
			String source = getVertexID(g.getEdgeSource(e));
			String target = getVertexID(g.getEdgeTarget(e));

			out.print(indent + source + connector + target);

			if (edgeLabelProvider != null) {
				out.print(" [label = \"" + edgeLabelProvider.getEdgeName(e)
						+ "\"]");
			}

			out.println(";");
		}

		out.println("}");

		out.flush();
	}

	/**
	 * Return a valid vertex ID (with respect to the .dot language definition as
	 * described in http://www.graphviz.org/doc/info/lang.html Quoted from above
	 * mentioned source: An ID is valid if it meets one of the following
	 * criteria:
	 * 
	 * <ul>
	 * <li>any string of alphabetic characters, underscores or digits, not
	 * beginning with a digit;
	 * <li>a number [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? );
	 * <li>any double-quoted string ("...") possibly containing escaped quotes
	 * (\");
	 * <li>an HTML string (<...>).
	 * </ul>
	 * 
	 * @throws RuntimeException
	 *             if the given <code>vertexIDProvider</code> didn't generate a
	 *             valid vertex ID.
	 */
	private String getVertexID(V v) {

		// use the associated id provider for an ID of the given vertex
		String idCandidate = vertexIDProvider.getVertexName(v);

		// now test that this is a valid ID
		boolean isName = idCandidate.matches("[a-zA-Z]+([\\-]?[a-zA-Z0-9_]+)*");
		//boolean isAlphaDig = idCandidate.matches("[a-zA-Z]*+([\\w_]*)?");
		//boolean isDoubleQuoted = idCandidate.matches("\".*\"");
		//boolean isDotNumber = idCandidate
		//		.matches("[-]?([.][0-9]+|[0-9]+([.][0-9]*)?)");
		//boolean isHTML = idCandidate.matches("<.*>");

		if (isName) {
			return idCandidate;
		}

		throw new RuntimeException("Generated id '" + idCandidate
				+ "'for vertex '" + v
				+ "' is not valid with respect to the .dot language.See reedme file.");
	}
}